#pragma once
#include "memory.h"

Memory memory;

void initMemory(){
  memory.begin();
}

size_t UsedMemory() {
    return memory.getUsedMemory();
}

void ClearMemory() {
    memory.clear();
}

void Memory::begin(){
    Logger::setLogLevel(Logger::VERBOSE);
    SDRAM.begin();
}

void* Memory::allocate(size_t size, uint8_t mode) {
    if (usedMemory + size > MAX_MEMORY) {
        Logger::error("Memory", "Memory allocation failed: not enough space");
        return nullptr;
    }

    void* ptr;

    if(mode == SRAM_M){
        ptr = malloc(size);
    }
    else if(mode == SDRAM_M){
        ptr = SDRAM.malloc(size);
    }

    if (ptr != nullptr) {
        usedMemory += size;
        MemoryBlock mb;
        mb.ptr = ptr;
        mb.blockSize = size;
        mb.mode = mode;
        allocations.push_back(mb);
        maxUsedMemory = maxUsedMemory < usedMemory ? usedMemory : maxUsedMemory;
        printMNoticeLog("Memory", size, " B memory allocated");
    } else {
        Logger::error("Memory", "Memory allocation failed!");
    }
    return ptr;
}

void Memory::deallocate(void* ptr) {
    if (ptr) {
      for (std::vector<MemoryBlock>::iterator it = allocations.begin(); it != allocations.end(); ++it) {
        if((*it).ptr == ptr){
            if((*it).mode == SRAM_M){
              free(ptr);
            }
            else if((*it).mode == SDRAM_M){
              SDRAM.free(ptr);
            }
            size_t size = (*it).blockSize;
            usedMemory -= size;
            allocations.erase(it);
            printMNoticeLog("Memory", size, " B memory deallocated");
            break;
        }
      }
    }
}

size_t Memory::getUsedMemory(){
  printMVerboseLog("Memory", usedMemory, " B is current usedmemory");
  return usedMemory;
}

void Memory::clear(){
  for (MemoryBlock mb : allocations) {
    if(mb.mode == SRAM_M){
      free(mb.ptr);
    }
    else if(mb.mode == SDRAM_M){
      SDRAM.free(mb.ptr);
    }
    usedMemory -= mb.blockSize;
  }
  allocations.clear();
  Logger::notice("Memory", "Memory clear finished");
}

void Memory::printMNoticeLog(const char *tag, size_t number, const char *message){

  String m = String(message);
  m = number + m + String(", total usedmemory is") + usedMemory + String(" B, remain usedmemory is ") + (MAX_MEMORY-usedMemory) + String(" B, maxUsedMemory is ") + maxUsedMemory;
  Logger::notice(tag, m.c_str());
}

void Memory::printMVerboseLog(const char *tag, size_t number, const char *message){
  String m = String(message);
  m = number + m;
  Logger::verbose(tag, m.c_str());
}

void* pMalloc(size_t n) {
    return memory.allocate(n, SDRAM_M);
}

void Free(void* ptr) {
    memory.deallocate(ptr);
}

Memory::~Memory(){
  clear();
}
